/******************************************************************************
* 文件名称: task.c
* 创建者の: 孔佳伟
* 个人主页: https://gitee.com/thin-wind/jump
* 修改记录: 
******************************************************************************/

#include "task.h"
#include "projdefs.h"
#include "port.h"


TCB_t* pxCurrentTCB = NULL;
 
// 任务就绪队列，数组的下标对应了任务的优先级，同一优先级的任务统一插入到就绪列表的同一条链表中
List_t pxReadyTasksLists[configMAX_PRIORITIES];

// 空闲任务栈
static StackType_t IdleTaskStack[configMINIMAL_STACK_SIZE];

// 空闲任务控制块
static TCB_t IdleTaskTCB;

// 空闲任务句柄
static TaskHandle_t xIdleTaskHandle = NULL;

// 用于 Systick 计数
static volatile TickType_t xTickCount = (TickType_t)0U;

// 定义任务就绪队列中任务的最高优先级，默认为优先级为 0，即空闲任务优先级
static volatile UBaseType_t uxTopReadyPriority = tskIDLE_PRIORITY;

// 任务计数
static volatile UBaseType_t uxCurrentNumberOfTasks = (UBaseType_t)0U;

// 任务延时列表 1，当系统时基计数器 xTickCount 没有溢出时，使用列表 1
static List_t xDelayedTaskList1;

// 任务延时列表 2，当系统时基计数器 xTickCount 溢出时，使用列表 2
static List_t xDelayedTaskList2;

// 任务延时列表指针，指向 xTickCount 没有溢出时使用的那条列表
static List_t* volatile pxDelayedTaskList;

// 任务延时列表指针，指向 xTickCount 溢出时使用的那条列表
static List_t* volatile pxOverflowDelayedTaskList; 

// 任务解锁时刻
static volatile TickType_t xNextTaskUnblockTime = (TickType_t)0U;

// xTimeToWake 溢出的任务计数
static volatile BaseType_t xNumOfOverflows = (BaseType_t)0;


// 查找最高优先级的就绪任务有两种方法，具体由 configUSE_PORT_OPTIMISED_TASK_SELECTION 这个宏控制
// 定义为 0 选择通用方法，定义为 1 选择根据处理器优化的方法 
// 该宏默认在 portmacro.h 中定义为 1，即使用优化过的方法
#if(configUSE_PORT_OPTIMISED_TASK_SELECTION == 0)
    
    // taskRECORD_READY_PRIORITY() 用于更新 uxTopReadyPriority 的值
    // uxTopReadyPriority 是一个在 task.c 中定义的静态变量
    // 用于表示创建的任务的最高优先级，默认初始化为 0
    #define taskRECORD_READY_PRIORITY(uxPriority)\
        {\
            if((uxPriority) > uxTopReadyPriority)\
            {\
                uxTopReadyPriority = (uxPriority);\
            }\
        } /* taskRECORD_READY_PRIORITY */
 
    // taskSELECT_HIGHEST_PRIORITY_TASK()用于寻找优先级最高的就绪任务
    // 实质就是更新 uxTopReadyPriority 和 pxCurrentTCB 的值
    #define taskSELECT_HIGHEST_PRIORITY_TASK()\
        {\
            UBaseType_t uxTopPriority = uxTopReadyPriority;\
            /* 寻找包含就绪任务的最高优先级的队列 */\
            /* 从最高优先级对应的就绪列表数组下标开始寻找当前链表下是否有任务存在 */\
            /* 如果没有，则 uxTopPriority 减一操作，继续寻找下一个优先级对应的链表中是否有任务存在 */\
            /* 如果有则跳出 while 循环，表示找到了最高优先级的就绪任务 */\
            /* 之所以可以采用从最高优先级往下搜索，是因为任务的优先级与就绪列表的下标是一一对应的 */\
            /* 优先级越高，对应的就绪列表数组的下标越大 */\
            while(listLIST_IS_EMPTY(&(pxReadyTasksLists[uxTopPriority])))\
            {\
                --uxTopPriority;\
            }\
            /* 获取优先级最高的就绪任务的 TCB，然后更新到 pxCurrentTCB */\
            listGET_OWNER_OF_NEXT_ENTRY(pxCurrentTCB, &(pxReadyTasksLists[uxTopPriority])); \
            /* 更新 uxTopReadyPriority */\
            uxTopReadyPriority = uxTopPriority;\
        } /* taskSELECT_HIGHEST_PRIORITY_TASK */
 
 
    // 这两个宏定义只有在选择优化方法时才用，这里定义为空
    #define taskRESET_READY_PRIORITY(uxPriority)
    #define portRESET_READY_PRIORITY(uxPriority, uxTopReadyPriority)
 
 // 查找最高优先级的就绪任务：根据处理器架构优化后的方法
 #else /* configUSE_PORT_OPTIMISED_TASK_SELECTION */
    
    // portRECORD_READY_PRIORITY() 作用是置位操作 set bit，只不过专门操作 uxTopReadyPriority 变量
    // portRECORD_READY_PRIORITY 与 portRESET_READY_PRIORITY 正好是一对
    // portRECORD_READY_PRIORITY 宏在 portmacro.h 中定义
    #define taskRECORD_READY_PRIORITY(uxPriority) \
        portRECORD_READY_PRIORITY(uxPriority, uxTopReadyPriority)
 
    // taskSELECT_HIGHEST_PRIORITY_TASK() 用于寻找优先级最高的就绪任务 
    #define taskSELECT_HIGHEST_PRIORITY_TASK()\
        {\
            UBaseType_t uxTopPriority;\
            /* 寻找最高优先级-利用 CLZ 指令 */\
            portGET_HIGHEST_PRIORITY(uxTopPriority, uxTopReadyPriority);\
            /* 获取优先级最高的就绪任务的 TCB，然后更新到 pxCurrentTCB */\
            listGET_OWNER_OF_NEXT_ENTRY(pxCurrentTCB, &( pxReadyTasksLists[uxTopPriority]));\
        } /* taskSELECT_HIGHEST_PRIORITY_TASK() */

    // taskRESET_READY_PRIORITY() 作用是位清 0 操作 reset bit，只不过专门操作 uxTopReadyPriority 变量
    #define taskRESET_READY_PRIORITY(uxPriority)\
        {\
            if(listCURRENT_LIST_LENGTH(&(pxReadyTasksLists[(uxPriority)])) == (UBaseType_t)0)\
            {\
                portRESET_READY_PRIORITY((uxPriority), (uxTopReadyPriority));\
            }\
        }
 
#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */

// 将任务添加到就绪列表
#define prvAddTaskToReadyList(pxTCB)\
    {\
        taskRECORD_READY_PRIORITY((pxTCB)->uxPriority);\
        vListInsertEnd(&(pxReadyTasksLists[(pxTCB)->uxPriority]), &((pxTCB)->xStateListItem));\
    }

// 切换延时列表，实际上就是更换 pxDelayedTaskList 和 pxOverflowDelayedTaskList 这两个指针的指向
#define taskSWITCH_DELAYED_LISTS()\
    {\
        List_t *pxTemp;\
        pxTemp = pxDelayedTaskList;\
        pxDelayedTaskList = pxOverflowDelayedTaskList;\
        pxOverflowDelayedTaskList = pxTemp;\
        xNumOfOverflows++;\
        prvResetNextTaskUnblockTime(); /* 复位 xNextTaskUnblockTime 的值*/\
    }

// 创建任务-本文件内部使用，其本质是实例化 TCB 任务控制块
static void prvInitialiseNewTask(TaskFunction_t pxTaskCode,         /* 任务入口 */
                                const char* const pcName,           /* 任务名称，字符串形式 */
                                const uint32_t ulStackDepth,        /* 任务栈大小，单位为字 */
                                void* const pvParameters,           /* 任务形参 */
                                UBaseType_t uxPriority,             /* 任务优先级 */
                                TaskHandle_t* const pxCreatedTask,  /* 任务栈起始地址 */
                                TCB_t* pxNewTCB                     /* 任务控制块指针 */
                                )
{
    StackType_t* pxTopOfStack;
    UBaseType_t x;

    // 获取栈顶地址，因为是减栈，入栈时数据是从高到低压入，所以栈顶指针初始时应该栈内存空间的最高地址处
    pxTopOfStack = pxNewTCB->pxStack + (ulStackDepth - (uint32_t)1);
    
    // 向下做 8 字节对齐，在 Cortex-M3(Cortex-M4 或 Cortex-M7)内核的单片机中，
    // 因为总线宽度是 32 位的，通常只要栈保持 4 字节对齐就行， 可这样为啥要 8 字节? 难道有哪些操作是 64 位的?
    // 确实有，那就是浮点运算，所以要 8 字 节对齐(但是目前我们都还没有涉及到浮点运算，只是为了后续兼容浮点运行的考虑)
    // 如果栈顶指针是 8 字节对齐的，在进行向下 8 字节对齐的时候，指针不会移动
    // 如果不是 8 字节对齐的，在做向下 8 字节对齐的时候，就会空出几个字节，不会使用
    // 比如当 pxTopOfStack 是 33，明显不能整除 8，进行向下 8 字节对齐就是 32，那么就会空出一个字节不使用
    pxTopOfStack = (StackType_t *)(((uint32_t)pxTopOfStack) & (~((uint32_t)0x0007)));

    // 将任务的名字存储在 TCB 中
    for (x = (UBaseType_t)0; x < (UBaseType_t )configMAX_TASK_NAME_LEN; x++)
    {
        pxNewTCB->pcTaskName[x] = pcName[x];
        if (pcName[x] == 0x00)
        {
            break;
        }
    }
    // 任务名字的长度不能超过 configMAX_TASK_NAME_LEN, 并以 '\0' 结尾
    pxNewTCB->pcTaskName[configMAX_TASK_NAME_LEN - 1] = '\0';

    // 初始化 TCB 中的 xStateListItem 节点
    vListInitialiseItem(&(pxNewTCB->xStateListItem));
    // 设置 xStateListItem 节点的拥有者，即拥有这个节点本身的 TCB
    listSET_LIST_ITEM_OWNER(&(pxNewTCB->xStateListItem), pxNewTCB);

    // 初始化优先级
    if(uxPriority >= (UBaseType_t)configMAX_PRIORITIES) 
    { 
        uxPriority = (UBaseType_t)configMAX_PRIORITIES - (UBaseType_t)1U; 
    } 
    pxNewTCB->uxPriority = uxPriority;

    // 初始化任务栈，并更新栈顶指针，任务第一次运行的环境参数就存在任务栈中
    pxNewTCB->pxTopOfStack = pxPortInitialiseStack(pxTopOfStack, pxTaskCode, pvParameters);
    
    // 让任务句柄指向任务控制块
    if ((void *)pxCreatedTask != NULL )
    {
        *pxCreatedTask = (TaskHandle_t) pxNewTCB;
    }
}

// 将任务添加到就绪列表
static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )
{
    taskENTER_CRITICAL();                   // 进入临界段

    uxCurrentNumberOfTasks++;               // 全局任务计数器加一

    // 如果 pxCurrentTCB 为空，则将 pxCurrentTCB 指向新创建的任务
    if(pxCurrentTCB == NULL)
    {
        pxCurrentTCB = pxNewTCB;

        // 如果是第一次创建任务，则需要初始化任务相关的列表
        if(uxCurrentNumberOfTasks == (UBaseType_t)1)
        {
            prvInitialiseTaskLists();       // 初始化任务相关的列表
        }
    }
    else /* 如果 pxCurrentTCB 不为空，则根据任务的优先级将 pxCurrentTCB 指向最高优先级任务的 TCB */
    {
        if(pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority)
        {
            pxCurrentTCB = pxNewTCB;
        }
    }

    prvAddTaskToReadyList(pxNewTCB);        // 将任务添加到就绪列表

    taskEXIT_CRITICAL();                    // 退出临界段
}

#if(configSUPPORT_STATIC_ALLOCATION == 1)
// 创建任务，其本质是实例化 TCB 任务控制块
TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,          /* 任务入口 */
                                const char* const pcName,           /* 任务名称，字符串形式 */
                                const uint32_t ulStackDepth,        /* 任务栈大小，单位为字 */
                                void* const pvParameters,           /* 任务形参 */
                                UBaseType_t uxPriority,             /* 任务优先级，数值越大，优先级越高 */
                                StackType_t* const puxStackBuffer,  /* 任务栈起始地址 */
                                TCB_t* const pxTaskBuffer           /* 任务控制块指针 */
                                )
{
    TCB_t *pxNewTCB; 
    TaskHandle_t xReturn = NULL;

    if ((pxTaskBuffer != NULL) && (puxStackBuffer != NULL))
    {
        pxNewTCB = (TCB_t *)pxTaskBuffer;
        pxNewTCB->pxStack = (StackType_t *)puxStackBuffer;

        /* 创建新的任务 */
        prvInitialiseNewTask( pxTaskCode,    /* 任务入口 */
                            pcName,          /* 任务名称，字符串形式 */
                            ulStackDepth,    /* 任务栈大小，单位为字 */
                            pvParameters,    /* 任务形参 */
                            uxPriority,      /* 任务优先级，数值越大，优先级越高 */
                            &xReturn,        /* 任务句柄 */
                            pxNewTCB         /* 任务栈起始地址 */
                           ); 
        
        // 将任务添加到就绪列表
        prvAddNewTaskToReadyList(pxNewTCB);
    }
    else
    {
        xReturn = NULL;
    }

    return xReturn;
}

#endif


// 就绪列表初始化
void prvInitialiseTaskLists(void)
{
    UBaseType_t uxPriority;

    for(uxPriority = (UBaseType_t)0U; uxPriority < (UBaseType_t)configMAX_PRIORITIES; uxPriority++)
    {
        vListInitialise(&(pxReadyTasksLists[uxPriority]));
    }

    // 任务延时列表初始化
    vListInitialise(&xDelayedTaskList1);
    vListInitialise(&xDelayedTaskList2);
    pxDelayedTaskList = &xDelayedTaskList1;
    pxOverflowDelayedTaskList = &xDelayedTaskList2;
}


static portTASK_FUNCTION(prvIdleTask, pvParameters)
{
	// 防止编译器的警告
	(void)pvParameters;
    
    for(;;)
    {
        ; // do nothing
    }
}

// 启动调度器
void vTaskStartScheduler(void)
{
    // 创建空闲任务
 	xIdleTaskHandle = xTaskCreateStatic((TaskFunction_t)prvIdleTask, 	/* 任务入口 */
									(char *)"IDLE", 				    /* 任务名称，字符串形式 */
									(uint32_t)configMINIMAL_STACK_SIZE, /* 任务栈大小，单位为字 */
									(void *) NULL, 					    /* 任务形参 */
									(UBaseType_t)tskIDLE_PRIORITY,      /* 任务优先级，数值越大，优先级越高 */
									(StackType_t *)IdleTaskStack, 		/* 任务栈起始地址 */
									(TCB_t *)&IdleTaskTCB); 			/* 任务控制块 */
	
    // 初始化 xNextTaskUnblockTime
    xNextTaskUnblockTime = portMAX_DELAY;

    // 启动调度器
    if (xPortStartScheduler() != pdFALSE )
    {
        // 调度器启动成功，则不会返回，即不会来到这里
    }
}


// 任务切换
void vTaskSwitchContext(void) 
{ 
    // 获取优先级最高的就绪任务的 TCB，然后更新到 pxCurrentTCB
    taskSELECT_HIGHEST_PRIORITY_TASK();
}

// 将任务插入到延时列表
static void prvAddCurrentTaskToDelayedList(TickType_t xTicksToWait)
{
    TickType_t xTimeToWake;

    // 获取系统时基计数器 xTickCount 的值，xTickCount 用于记录 SysTick 的中断次数
    const TickType_t xConstTickCount = xTickCount;

    // 将任务从就绪列表中移除
    // uxListRemove()会返回当前链表下节点的个数，如果为 0，则表示当前链表下没有任务就绪
    // 则调用 portRESET_READY_PRIORITY() 将任务在优先级位图表 uxTopReadyPriority 中对应的位清除
    // 因为同一优先级下可以有多个任务，所以只有当该优先级的就绪列表中没有任务时才能够清楚优先级位图 uxTopReadyPriority
    if (uxListRemove(&(pxCurrentTCB->xStateListItem)) == (UBaseType_t )0)
    {
        // 将任务在优先级位图中对应的位清除
        portRESET_READY_PRIORITY(pxCurrentTCB->uxPriority, uxTopReadyPriority);
    }

    // 计算任务延时到期时，系统时基计数器 xTickCount 的值是多少
    xTimeToWake = xConstTickCount + xTicksToWait;

    // 将延时到期的值设置为节点的排序值，将任务插入到延时列表时就是根据这个值来做升序排列的，最先延时到期的任务排在最前面
    listSET_LIST_ITEM_VALUE(&(pxCurrentTCB->xStateListItem ), xTimeToWake);

    // xTimeToWake 溢出，将任务插入到溢出任务延时列表
    // 溢出？什么意思？
    // xTimeToWake 等于系统时基计数器 xTickCount 的值加上任务需要延时的时间 xTicksToWait
    // 举例：如果当前 xTickCount 的值等于 0xfffffffdUL，xTicksToWait 等于 0x03，那么 xTimeToWake = 0xfffffffdUL + 0x03 = 1
    // 显然得出的值比任务需要延时的时间 0x03 还小，这肯定不正常，说明溢出了，这个时候需要将任务插入到溢出任务延时列表
    if (xTimeToWake < xConstTickCount)
    {
        // xTimeToWake 溢出，则将任务插入到溢出的那条任务延时列表
        vListInsert(pxOverflowDelayedTaskList, &(pxCurrentTCB->xStateListItem));
    }
    else // 没有溢出
    {
        // xTimeToWake 没有溢出，则将任务插入到正常任务延时列表
        vListInsert(pxDelayedTaskList, &(pxCurrentTCB->xStateListItem));

        // 更新下一个任务解锁时刻变量 xNextTaskUnblockTime 的值
        // 在 xTaskIncrementTick() 函数中，我们只需要让系统时基计数器 xTickCount 与 xNextTaskUnblockTime 的值先比较就知道延时最快结束的任务是否到期
        if (xTimeToWake < xNextTaskUnblockTime)
        {
            xNextTaskUnblockTime = xTimeToWake;
        }
    }
}


// 任务阻塞延时
void vTaskDelay(const TickType_t xTicksToDelay)
{
    TCB_t *pxTCB = NULL;

    // 获取当前任务的 TCB
    pxTCB = pxCurrentTCB;

    // 设置延时时间
    // pxTCB->xTicksToDelay = xTicksToDelay;

    // 将任务插入到延时列表
    prvAddCurrentTaskToDelayedList(xTicksToDelay);

    // 触发 PendSV 中断，切换任务
    taskYIELD();
}

// 复位 xNextTaskUnblockTime 的值
static void prvResetNextTaskUnblockTime(void)
{
    TCB_t *pxTCB;

    if (listLIST_IS_EMPTY(pxDelayedTaskList) != pdFALSE)
    {
        // 当前延时列表为空，则设置 xNextTaskUnblockTime 等于最大值
        xNextTaskUnblockTime = portMAX_DELAY;
    }
    else
    {
        // 当前列表不为空，则有任务在延时，则获取当前列表下第一个节点的排序值，然后将该节点的排序值更新到 xNextTaskUnblockTime
        pxTCB = (TCB_t *)listGET_OWNER_OF_HEAD_ENTRY(pxDelayedTaskList);
        xNextTaskUnblockTime = listGET_LIST_ITEM_VALUE(&((pxTCB)->xStateListItem));
    }
}


// 更新系统时基
BaseType_t xTaskIncrementTick(void)     // 改动处 (1)
{
    TCB_t * pxTCB;
    TickType_t xItemValue;
    BaseType_t xSwitchRequired = pdFALSE;  // 改动处 (2)

    const TickType_t xConstTickCount = xTickCount + 1;
    xTickCount = xConstTickCount;

    // 如果 xConstTickCount 溢出，则切换延时列表
    if (xConstTickCount == (TickType_t)0U) 
    {
        taskSWITCH_DELAYED_LISTS();     // (1)
    } 

    // 最近的延时任务延时到期
    if (xConstTickCount >= xNextTaskUnblockTime) 
    { 
        // 有任务延时到期，则进入下面的 for 循环，一一将这些延时到期的任务从延时列表中移除
        for ( ;; ) 
        { 
            if (listLIST_IS_EMPTY(pxDelayedTaskList) != pdFALSE)
            { 
                // 延时列表为空，设置 xNextTaskUnblockTime 为可能的最大值
                xNextTaskUnblockTime = portMAX_DELAY; 
                break; 
            } 
            else // 延时列表不为空
            { 
                // 延时列表不为空，则有任务正在延时，获取当前第一个节点的排序值，然后将该值更新到 xNextTaskUnblockTime
                pxTCB = (TCB_t *)listGET_OWNER_OF_HEAD_ENTRY(pxDelayedTaskList); 
                xItemValue = listGET_LIST_ITEM_VALUE(&(pxTCB->xStateListItem));

                // 直到将延时列表中所有延时到期的任务移除才跳出 for 循环
                if (xConstTickCount < xItemValue) 
                { 
                    xNextTaskUnblockTime = xItemValue; 
                    break; 
                }

                // 将任务从延时列表移除，消除等待状态
                (void)uxListRemove(&(pxTCB->xStateListItem)); 

                // 将解除等待的任务添加到就绪列表
                prvAddTaskToReadyList(pxTCB);

                // 改动处 (3)
                #if(configUSE_PREEMPTION == 1)
                { 
                    if (pxTCB->uxPriority >= pxCurrentTCB->uxPriority) 
                    { 
                        xSwitchRequired = pdTRUE; 
                    } 
                } 
                #endif
            } 
        } 
    } 

    // 改动处 (4) 
    #if((configUSE_PREEMPTION == 1) && (configUSE_TIME_SLICING == 1))
    { 
        if(listCURRENT_LIST_LENGTH(&(pxReadyTasksLists[pxCurrentTCB->uxPriority])) > (UBaseType_t)1) 
        { 
            xSwitchRequired = pdTRUE; 
        } 
    } 
    #endif

    // 任务切换
    // portYIELD();     // 改动处 (5)

    return xSwitchRequired; // 改动处 (6)
}

